﻿using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CommonNet48.Timer
{
    /// <summary>
    /// 时间等待帮助类
    /// </summary>
    public class MyTimer
    {
        /// <summary>
        /// 终止业务委托
        /// </summary>
        public Func<bool> AbortFun { get; set; }

        /// <summary>
        /// 延时
        /// </summary>
        /// <param name="ms">延时时间，单位毫秒。</param>
        /// <returns></returns>
        public bool Delay(long ms)
        {
            bool abortFlag = false;
            Stopwatch sw = Stopwatch.StartNew();
            while (sw.ElapsedMilliseconds < ms)
            {
                if (AbortFun != null)//终止处理
                {
                    abortFlag = AbortFun();
                    if (abortFlag) return false;
                }
                Application.DoEvents();
            }
            sw.Stop();
            if (abortFlag) return false;//检查终止标记
            return true;
        }

        /// <summary>
        /// 延时(异步)
        /// </summary>
        /// <param name="ms"></param>
        /// <returns></returns>
        public async Task<bool> DelayAsync(long ms)
        {
            return await Task.Run<bool>(() =>
            {
                bool abortFlag = false;
                Stopwatch sw = Stopwatch.StartNew();
                while (sw.ElapsedMilliseconds < ms)
                {
                    if (AbortFun != null)//终止处理
                    {
                        abortFlag = AbortFun();
                        if (abortFlag) return false;
                    }
                    Application.DoEvents();
                }
                sw.Stop();
                if (abortFlag) return false;//检查终止标记
                return true;
            });
        }

        /// <summary>
        /// 等待业务返回True
        /// </summary>
        /// <param name="funBusiness">查询/等待业务委托</param>
        /// <param name="outtime">超时时间，单位毫秒。</param>
        /// <param name="interval">业务查询间隔，单位毫秒。</param>
        /// <returns></returns>
        public bool WaitingBusinessTrue(Func<bool> funBusiness, long outtime, long interval = 0)
        {
            bool? isAbort = null;
            Stopwatch sw = Stopwatch.StartNew();
            while (sw.ElapsedMilliseconds <= outtime)
            {
                isAbort = AbortFun?.Invoke();
                if (isAbort.HasValue && isAbort.Value) return false;//终止退出
                if (funBusiness()) return true;
                if (interval > 0) Delay(interval);//间隔多久执行一次(支持Abort)
                Application.DoEvents();
            }
            sw.Stop();
            if (isAbort.HasValue && isAbort.Value) return false;//结束前检测一下是否强行终止退出
            if (sw.ElapsedMilliseconds > outtime) return false;//超时
            return true;
        }

        /// <summary>
        /// 等待业务返回True(异步)
        /// </summary>
        /// <param name="funBusiness">查询/等待业务委托</param>
        /// <param name="outtime">超时时间，单位毫秒。</param>
        /// <param name="interval">业务查询间隔，单位毫秒。</param>
        /// <returns></returns>
        public async Task<bool> WaitingBusinessTrueAsync(Func<bool> funBusiness, long outtime, long interval = 0)
        {
            bool? isAbort = null;
            Stopwatch sw = Stopwatch.StartNew();
            while (sw.ElapsedMilliseconds <= outtime)
            {
                isAbort = AbortFun?.Invoke();
                if (isAbort.HasValue && isAbort.Value) return false;//终止退出
                if (funBusiness()) return true;
                if (interval > 0) await DelayAsync(interval);//间隔多久执行一次(支持Abort)
                Application.DoEvents();
            }
            sw.Stop();
            if (isAbort.HasValue && isAbort.Value) return false;//结束前检测一下是否强行终止退出
            if (sw.ElapsedMilliseconds > outtime) return false;//超时
            return true;
        }

        /// <summary>
        /// 等待业务返回数据
        /// </summary>
        /// <typeparam name="T">返回数据类型</typeparam>
        /// <param name="funBusiness">查询/等待业务委托</param>
        /// <param name="outtime">超时时间</param>
        /// <param name="interval">业务查询间隔，单位毫秒。</param>
        /// <returns></returns>
        public T WaitingBusinessRet<T>(Func<T> funBusiness, long outtime, long interval = 0) where T : class, new()
        {
            bool? isAbort = null;
            Stopwatch sw = Stopwatch.StartNew();
            while (sw.ElapsedMilliseconds <= outtime)
            {
                isAbort = AbortFun?.Invoke();
                if (isAbort.HasValue && isAbort.Value) return null;//终止退出
                T result = funBusiness?.Invoke();
                if (result != null) return result;
            }
            sw.Stop();
            return null;
        }

    }
}
